﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace GeoUtils
{
    public class GeoEntityStorage
    {
        List<List<GeoEntity>> geoItemListList;

        public GeoEntityStorage()
        {
            geoItemListList = new List<List<GeoEntity>>();
        }

        public void AddNewItem(GeoEntity entity)
        {
            geoItemListList.Add(new List<GeoEntity>());
            geoItemListList[geoItemListList.Count - 1].Add(entity);
        }

        public void AddNewItemAt(int idx, GeoEntity entity)
        {
            geoItemListList[idx].Add(entity);
        }

        public void AddNewItem(string strName, string strTag, bool bRewritten, List<int> iAdminCode, int iLevel, LatLong ll)
        {
            GeoEntity item = new GeoEntity(strName, strTag, bRewritten, iAdminCode, 0, iLevel, ll, null);
            geoItemListList.Add(new List<GeoEntity>());
            geoItemListList[geoItemListList.Count - 1].Add(item);
        }

        public void AddNewItemAt(int idx, string strName, string strTag, bool bRewritten, List<int> iAdminCode, int iLevel, LatLong ll)
        {
            GeoEntity item = new GeoEntity(strName, strTag, bRewritten, iAdminCode, 0, iLevel, ll, null);
            geoItemListList[idx].Add(item);
        }

        //Load geo-entity list from binary file
        public List<List<GeoEntity>> LoadFromFile(string strFileName)
        {
            FileStream fs = new FileStream(strFileName, FileMode.Open, FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);
            geoItemListList = new List<List<GeoEntity>>();

            //The total unique geo-entity name number
            int iListListCount = br.ReadInt32();

            for (int k = 0;k < iListListCount;k++)
            {
                int iListCnt = br.ReadInt32();
                geoItemListList.Add(new List<GeoEntity>());
                for (int i = 0; i < iListCnt; i++)
                {
                    string strGeoEntityName = br.ReadString();
                    string strTag = br.ReadString();
                    bool bRewritten = br.ReadBoolean();

                    //Admin code list
                    int iAdminCodeCnt = br.ReadInt32();
                    List<int> adminCodeList = new List<int>();
                    for (int j = 0; j < iAdminCodeCnt; j++)
                    {
                        adminCodeList.Add(br.ReadInt32());
                    }
                    
                    int iAdminPreCode = br.ReadInt32();
                    int iLevel = br.ReadInt32();
                    double X = br.ReadDouble();
                    double Y = br.ReadDouble();
                    BoundPolygon boundPolygon = null;

                    //Vector data
                    int polyNum = br.ReadInt32();
                    if (polyNum > 0)
                    {
                        BoundPolygonType type = BoundPolygonType.POINT;
                        double[][] latitudes;
                        double[][] longitudes;

                        latitudes = new double[polyNum][];
                        longitudes = new double[polyNum][];

                        int iType = br.ReadInt32();
                        switch (iType)
                        {
                            case 0:
                                type = BoundPolygonType.POLYGON;
                                break;
                            case 1:
                                type = BoundPolygonType.LINE;
                                break;
                            case 2:
                                type = BoundPolygonType.POINT;
                                break;
                        }

                        for (int j = 0; j < polyNum; j++)
                        {
                            int pointNum = br.ReadInt32();
                            latitudes[j] = new double[pointNum];
                            longitudes[j] = new double[pointNum];

                            for (int z = 0; z < pointNum; z++)
                            {
                                latitudes[j][z] = br.ReadDouble();
                                longitudes[j][z] = br.ReadDouble();
                            }
                        }

                        boundPolygon = new BoundPolygon(latitudes, longitudes, type);

                    }

                    GeoEntity geoEntity = new GeoEntity(strGeoEntityName, strTag, bRewritten, adminCodeList, iAdminPreCode, iLevel, new LatLong(X, Y), boundPolygon);
                    geoItemListList[geoItemListList.Count - 1].Add(geoEntity);
                }

            }
            br.Close();

            return geoItemListList;
        }

        public void SaveIntoFile(string strFileName)
        {
            StreamWriter sw = new StreamWriter(strFileName);
            BinaryWriter bw = new BinaryWriter(sw.BaseStream);

            bw.Write(geoItemListList.Count);
            foreach (List<GeoEntity> geoItemList in geoItemListList)
            {
                bw.Write(geoItemList.Count);

                foreach (GeoEntity item in geoItemList)
                {
                    bw.Write(item.Name);
                    bw.Write(item.Tag);
                    bw.Write(item.bRewritten);

                    //if (item.AliasList == null)
                    //{
                    //    bw.Write(0);
                    //}
                    //else
                    //{
                    //    bw.Write(item.AliasList.Count);
                    //    for (int i = 0; i < item.AliasList.Count; i++)
                    //    {
                    //        bw.Write(item.AliasList[i]);
                    //    }

                    //}


                    bw.Write(item.AdminCodeList.Count);
                    for (int i = 0; i < item.AdminCodeList.Count; i++)
                    {
                        bw.Write(item.AdminCodeList[i]);
                    }
                    
                    bw.Write(item.AdminPreCode);
                    bw.Write(item.iLevel);
                    bw.Write(item.LatLong.Latitude);
                    bw.Write(item.LatLong.Longitude);

                    if (item.BoundingPolygon == null)
                    {
                        bw.Write(0);
                    }
                    else
                    {
                        int polyNum = item.BoundingPolygon.Latitudes.GetLength(0);
                        bw.Write(polyNum);

                        switch (item.BoundingPolygon.Type)
                        {
                            case BoundPolygonType.POLYGON:
                                bw.Write(0);
                                break;
                            case BoundPolygonType.LINE:
                                bw.Write(1);
                                break;
                            case BoundPolygonType.POINT:
                                bw.Write(2);
                                break;
                        }

                        for (int i = 0; i < polyNum; i++)
                        {
                            int pointNum = item.BoundingPolygon.Latitudes[i].Length;
                            bw.Write(pointNum);

                            for (int j = 0; j < pointNum; j++)
                            {
                                bw.Write(item.BoundingPolygon.Latitudes[i][j]);
                                bw.Write(item.BoundingPolygon.Longitudes[i][j]);
                            }
                        }
                    }
                }
            }
            bw.Close();
        }
    }
}
